<template>
  <div class="container">
    <div class="editor" ref="el"></div>
    <div class="editor" ref="el2"></div>
  </div>
  <div class="play" @click="run()"></div>
</template>

<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
import { format } from 'date-fns';
import * as monaco from 'monaco-editor';

function createEditor(
  el: HTMLElement,
  options: monaco.editor.IStandaloneEditorConstructionOptions = {},
) {
  const editor = monaco.editor.create(el, {
    language: 'javascript',
    theme: 'vs-dark',
    tabSize: 2,
    minimap: { enabled: false },
    automaticLayout: true,
    ...options,
  });
  return {
    getValue: () => editor.getValue(),
    setValue: (d: string) => editor.setValue(d),
    addAction: (d: any) => editor.addAction(d),
    getLineCount: () => editor.getModel()?.getLineCount(),
    revealLine: (d: number) => editor.revealLine(d),
    format: () => editor.getAction('editor.action.formatDocument').run(),
    dispose: () => editor.dispose(),
  };
}

export default defineComponent({
  name: 'HelloWorld',
  setup() {
    const el = ref();
    const el2 = ref();
    const editor = ref();
    const result = ref();

    const run = () => {
      if (editor.value) {
        try {
          editor.value.format();
          result.value.setValue('');
          Function(`(function(){${editor.value.getValue()}})()`)();
        } catch (error) {
          console.log(error.message);
        }
      }
    };

    const genericLogger = () => {
      return function () {
        if (result.value) {
          const value = [].slice
            .call(arguments)
            .reduce(
              (prev, arg) => `${prev} ${typeof arg === 'object' ? JSON.stringify(arg) : arg}`,
              '',
            );
          const time = `[${format(new Date(), 'HH:mm:ss.SSS')}] `;
          result.value.setValue(result.value.getValue() + time + value + '\n');
          result.value.revealLine(result.value.getLineCount() || 0);
        }
      };
    };

    const initLogger = () => {
      console.log = function () {
        return genericLogger().apply(null, arguments as any);
      };
      console.info = function () {
        return genericLogger().apply(null, arguments as any);
      };
      console.warn = function () {
        return genericLogger().apply(null, arguments as any);
      };
      console.error = function () {
        return genericLogger().apply(null, arguments as any);
      };
    };

    onMounted(() => {
      if (el.value) {
        editor.value = createEditor(el.value);
        editor.value.addAction({
          id: 'run', // 菜单项 id
          label: '运行', // 菜单项名称
          keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_R], // 绑定快捷键
          contextMenuGroupId: 'navigation', // 所属菜单的分组
          run: () => run(), // 点击后执行的操作
        });
      }
      if (el2.value) {
        result.value = createEditor(el2.value, { readOnly: true });
      }
      initLogger();
    });

    onUnmounted(() => {
      if (editor.value) {
        editor.value.dispose();
      }
      if (result.value) {
        result.value.dispose();
      }
    });

    return { el, el2, run };
  },
});
</script>

<style scoped lang="less">
.container {
  display: flex;
}
.editor {
  width: 50vw;
  height: 100vh;
}
.play {
  position: fixed;
  bottom: 24px;
  right: 40px;
  width: 40px;
  height: 40px;
  background: url('../assets/play.png');
  background-size: cover;
  cursor: pointer;
}
</style>
